/////////////////////////////////////////////////////////////////////
//	$file: $
//	$revision: $
//	$author: $
//	$datetime: $
//	first author: buggy

#include	<QDir>
#include	<QHeaderView>
#include	<QKeyEvent>

#include	<QDebug>

#include	"SourceNavigatorApp.h"
#include	"SourceNavigator.h"
#include	"SourceTree.h"

#define	KEYPAD_MODIFIER		Qt::KeypadModifier
#define	CTRL_MODIFIER		Qt::MetaModifier
#define	META_MODIFIER		Qt::ControlModifier

///////////////////////////////////////////////////////////
//	Source Navigator Widget
const	SourceTree::FuncKeyMap		SourceTree::_KeyMap[SourceTree::_KeyMapMax]	=
{
	//	up
	{	KEYPAD_MODIFIER,		Qt::Key_Up,			Qt::Key_Up			},
	{	Qt::NoModifier,			Qt::Key_K,			Qt::Key_Up			},
	//	down
	{	KEYPAD_MODIFIER,		Qt::Key_Down,		Qt::Key_Down		},
	{	Qt::NoModifier,			Qt::Key_J,			Qt::Key_Down		},
	//	top
	{	Qt::NoModifier,			Qt::Key_Home,		Qt::Key_Home		},
	//	bottom
	{	Qt::NoModifier,			Qt::Key_End,		Qt::Key_End			},
	//	forward page
	{	Qt::NoModifier,			Qt::Key_PageDown,	Qt::Key_PageDown	},
	{	CTRL_MODIFIER,			Qt::Key_F,			Qt::Key_PageDown	},
	//	backward page
	{	Qt::NoModifier,			Qt::Key_PageUp,		Qt::Key_PageUp		},
	{	CTRL_MODIFIER,			Qt::Key_B,			Qt::Key_PageUp		},

	//	default for QTreeWidget
	{	Qt::NoModifier,			Qt::Key_Plus,		Qt::Key_Plus		},
	{	Qt::ShiftModifier,		Qt::Key_Plus,		Qt::Key_Plus		},
	{	Qt::NoModifier,			Qt::Key_Minus,		Qt::Key_Minus		},
};


SourceTree::SourceTree(const QString& rootPath, SourceNavigator* parentWidget)
	:	QTreeWidget()
{
	_rootPath	= rootPath;
	_parentWidget	= parentWidget;

	setColumnCount(1);
	setIndentation(10);
	header()->hide();

	Refresh();
}

SourceTree::~SourceTree()
{
}

#define	RootItem()		(static_cast<SourceTreeItem*>(topLevelItem(0)))

void SourceTree::Refresh()
{
	SourceTreeItem*		root	= RootItem();

	if (NULL != root)
	{
		delete root;
	}

	root	= SourceTreeItem::Create(QFileInfo(_rootPath).absoluteFilePath(), NULL);
	if (NULL != root)
	{
		addTopLevelItem(root);
		expandAll();
		setCurrentItem(root);
	}
}

bool SourceTree::IsValid() const
{
	return NULL != RootItem();
}

QString SourceTree::RootName() const
{
	if (NULL == RootItem())
		return QString();
	return RootItem()->FileName();
}

QString SourceTree::RootPath() const
{
	if (NULL == RootItem())
		return QString();
	return RootItem()->AbsolutePath();
}

void SourceTree::GoTop()
{
	QKeyEvent	e(QKeyEvent::KeyPress, Qt::Key_Home, Qt::NoModifier);
	QTreeWidget::keyPressEvent(&e);
}

void SourceTree::GoBottom()
{
	QKeyEvent	e(QKeyEvent::KeyPress, Qt::Key_End, Qt::NoModifier);
	QTreeWidget::keyPressEvent(&e);
}

/*
void SourceTree::moveUp()
{
	QKeyEvent	e(QKeyEvent::KeyPress, Qt::Key_Up, Qt::NoModifier);
	QTreeWidget::keyPressEvent(&e);
}

void SourceTree::moveDown()
{
	QKeyEvent	e(QKeyEvent::KeyPress, Qt::Key_Down, Qt::NoModifier);
	QTreeWidget::keyPressEvent(&e);
}

void SourceTree::moveTop()
{
	QKeyEvent	e(QKeyEvent::KeyPress, Qt::Key_Home, Qt::NoModifier);
	QTreeWidget::keyPressEvent(&e);
}

void SourceTree::moveBottom()
{
	QKeyEvent	e(QKeyEvent::KeyPress, Qt::Key_End, Qt::NoModifier);
	QTreeWidget::keyPressEvent(&e);
}

void SourceTree::backwardPage()
{
	QKeyEvent	e(QKeyEvent::KeyPress, Qt::Key_Up, Qt::NoModifier);
	QTreeWidget::keyPressEvent(&e);
}

void SourceTree::forwardPage()
{
	QKeyEvent	e(QKeyEvent::KeyPress, Qt::Key_PageDown, Qt::NoModifier);
	QTreeWidget::keyPressEvent(&e);
}

void SourceTree::collpse()
{
	QKeyEvent	e(QKeyEvent::KeyPress, Qt::Key_Left, Qt::NoModifier);
	QTreeWidget::keyPressEvent(&e);
}

void SourceTree::expand()
{
	QKeyEvent	e(QKeyEvent::KeyPress, Qt::Key_Right, Qt::NoModifier);
	QTreeWidget::keyPressEvent(&e);
}
*/


bool SourceTree::open()
{
	int		pos	= 0;
	const	SourceTreeItem*	src	= static_cast<const SourceTreeItem*>(currentItem());

	if (!src->CanOpen())
		return false;


	switch (src->Type())
	{
		case SourceTreeItem::HEADER:		pos	= 1;	break;
		case SourceTreeItem::C_SOURCE:		pos	= 4;	break;
		case SourceTreeItem::CPP_SOURCE:	pos	= 4;	break;
		default:							pos	= 5;
	}
	_parentWidget->OpenTextEditor(src, pos);
	return true;
}

void SourceTree::mouseDoubleClickEvent(QMouseEvent* event)
{
	if (false == open())
		QTreeWidget::mouseDoubleClickEvent(event);
}

void SourceTree::keyPressEvent(QKeyEvent* event)
{
	if (!_parentWidget->IsNavigationMode())
	{
		_parentWidget->ForwardKeyEventToCommand(event);
		return;
	}

	//	open
	if ( Qt::NoModifier == event->modifiers() 
			&& (Qt::Key_Return == event->key() || Qt::Key_Enter == event->key()) )
	{
		if (false == open())
			QTreeWidget::keyPressEvent(event);
		return;
	}

	//	refresh
	if ( (CTRL_MODIFIER == event->modifiers() && Qt::Key_R == event->key()) ||
		(META_MODIFIER == event->modifiers() && Qt::Key_R == event->key()) ||
		(Qt::NoModifier == event->modifiers() && Qt::Key_F5 == event->key()) )
	{
		Refresh();
	}

	//	command
	if ( ":" == event->text())
	{
		_parentWidget->TransitMode(event);
		return;
	}
	//	search
	if ( "/" == event->text())
	{
		_parentWidget->TransitMode(event);
		return;
	}


	for (int i = 0; i < _KeyMapMax; ++i)
	{
		if (_KeyMap[i].key == 0)
			break;
		if (_KeyMap[i].modifier == event->modifiers() && _KeyMap[i].key == event->key())
		{
			QKeyEvent	e(QKeyEvent::KeyPress, _KeyMap[i].funcKey, Qt::NoModifier);

			QTreeWidget::keyPressEvent(&e);
			break;
		}
	}
}


///////////////////////////////////////////////////////////
//	Source Navigator Widget Item
QVector<QIcon*> SourceTreeItem::_Icons;

SourceTreeItem* SourceTreeItem::Create(const QString& path, SourceTreeItem* parent)
{
	SourceTreeItem*	src	= new SourceTreeItem(QFileInfo(path));

	if (false == src->Initialize())
	{
		delete src;
		return NULL;
	}

	if (NULL != parent)
	{
		if (src->Type() == DIR && src->childCount() == 0)
		{
			//	remove empty directory
			delete src;
			return NULL;
		}
		else
		{
			parent->addChild(src);
		}
	}
	return src;
}

QIcon SourceTreeItem::Icon(TYPE type)
{
	if (_Icons.isEmpty())
	{
		_Icons.resize(SourceTreeItem::TYPE_MAX);
		_Icons[SourceTreeItem::UNKNOWN]		= new QIcon();
		_Icons[SourceTreeItem::FILE]		= new QIcon(":/image/file.png");
		_Icons[SourceTreeItem::DIR]			= new QIcon(":/image/dir-white.png");
		_Icons[SourceTreeItem::DIR_BIN]		= new QIcon();
		_Icons[SourceTreeItem::DIR_BUILD]	= new QIcon();
		_Icons[SourceTreeItem::C_SOURCE]	= new QIcon(":/image/source_c.png");
		_Icons[SourceTreeItem::CPP_SOURCE]	= new QIcon(":/image/source_cpp.png");
		_Icons[SourceTreeItem::HEADER]		= new QIcon(":/image/source_h.png");
		_Icons[SourceTreeItem::MAKEFILE]	= new QIcon(":/image/source_makefile.png");
	}

	return QIcon( *(_Icons.value(type, _Icons[SourceTreeItem::UNKNOWN])) );
}

SourceTreeItem::SourceTreeItem(const QFileInfo& fileinfo)
	:	QTreeWidgetItem(QTreeWidgetItem::UserType + 1)
{
	_type		= SourceTreeItem::UNKNOWN;
	_state		= SourceTreeItem::NORMAL;
	_fileinfo	= fileinfo;
}

SourceTreeItem::~SourceTreeItem()
{
}

bool SourceTreeItem::Initialize()
{
	if (SourceTreeItem::UNKNOWN != _type)
		return true;

	if (!_fileinfo.exists())
		return false;

	if (_fileinfo.isDir())
	{
		if (_fileinfo.suffix() == "app")
			_type	= SourceTreeItem::DIR_BIN;
		else if (_fileinfo.baseName() == "_build")
			_type	= SourceTreeItem::DIR_BUILD;
		else
			_type	= SourceTreeItem::DIR;
	}
	else if (_fileinfo.isFile())
	{
		if (_fileinfo.suffix() == "h" || _fileinfo.suffix() == "hpp")
			_type	= SourceTreeItem::HEADER;
		else if (_fileinfo.suffix() == "c")
			_type	= SourceTreeItem::C_SOURCE;
		else if (_fileinfo.suffix() == "cpp" || _fileinfo.suffix() == "cc")
			_type	= SourceTreeItem::CPP_SOURCE;
		else if (_fileinfo.baseName() == "Makefile" || _fileinfo.baseName() == "makefile")
			_type	= SourceTreeItem::MAKEFILE;
		else
			_type	= SourceTreeItem::FILE;
	}
	else
	{
		return false;
	}

	if (_type == SourceTreeItem::DIR_BIN || _type == SourceTreeItem::DIR_BUILD || _type == SourceTreeItem::FILE)
		return false;

	if (_type == SourceTreeItem::DIR)
	{
		QDir			dir(_fileinfo.absoluteFilePath());
		QFileInfoList	list	= dir.entryInfoList(
									QDir::Dirs | QDir::NoDotAndDotDot | QDir::Files,
									QDir::DirsFirst | QDir::Name);

		for (int i = 0; i < list.count(); ++i)
			Create(list.at(i).absoluteFilePath(), this);
	}

	return true;
}


QVariant SourceTreeItem::data(int column, int role) const
{
	if (0 != column)
		return QVariant();

	if (role == Qt::DisplayRole)
	{
		return _fileinfo.fileName();
	}
	else if (role == Qt::DecorationRole)
	{
		return Icon();
	}

	return QVariant();
}

bool SourceTreeItem::CanOpen() const
{
	return _type == SourceTreeItem::C_SOURCE || _type == SourceTreeItem::CPP_SOURCE
		|| _type == SourceTreeItem::HEADER || _type == SourceTreeItem::MAKEFILE;
}

QString SourceTreeItem::Id() const
{
	SourceTree*		tree	= static_cast<SourceTree*>(treeWidget());

	return QString("%1:/%2").arg(tree->RootName())
							.arg(AbsolutePath().remove(tree->RootPath()));
}

QString SourceTreeItem::AbsolutePath() const
{
	return _fileinfo.absoluteFilePath();
}

QString SourceTreeItem::FileName() const
{
	return _fileinfo.fileName();
}

QIcon SourceTreeItem::Icon() const
{
	return Icon(_type);
}
